/**
 * ClientFactoryImpl.java<br>
 *
 *
 * @version 下午10:08:37
 */
package com.iwechat.service.impl;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.iwechat.mybatis.generator.dao.TmpsClientConfigsMapper;
import com.iwechat.mybatis.generator.dao.TmpsIpWhiteListMapper;
import com.iwechat.mybatis.generator.model.TmpsClientConfigs;
import com.iwechat.mybatis.generator.model.TmpsIpWhiteList;
import com.iwechat.service.ClientFactory;
import com.iwechat.service.RefreshAble;

/**
 * 客户端配置工厂
 *
 *
 */
@Service("clientfc")
public class ClientFactoryImpl implements ClientFactory, RefreshAble {
    private static final Logger           logger = LogManager.getLogger();

    @Autowired
    TmpsClientConfigsMapper               cs;
    @Autowired
    TmpsIpWhiteListMapper                 ciws;

    private volatile Map<String, Integer> c_maxConn_map;
    private volatile Map<String, Integer> c_window_map;
    private volatile Map<String, String>  c_publicKey_map;
    private volatile Set<String>          c_ip_set;

    @Override
    public void refresh() {
        List<TmpsClientConfigs> ccs = cs.getAllCient();
        Map<String, Integer> c_maxConn_map = new Hashtable<String, Integer>();
        Map<String, Integer> c_window_map = new Hashtable<String, Integer>();
        Map<String, String> c_publicKey_map = new Hashtable<String, String>();
        for (TmpsClientConfigs c : ccs) {
            c_maxConn_map.put(c.getClientId(), c.getMaxConn());
            c_window_map.put(c.getClientId(), c.getSrWindow());
            c_publicKey_map.put(c.getClientId(), c.getPublicKey());
        }

        Set<String> c_ip_set = new HashSet<String>();
        List<TmpsIpWhiteList> lciws = ciws.getAll();
        for (TmpsIpWhiteList c : lciws) {
            c_ip_set.add(c.getIp());
        }

        synchronized (this) {
            // 加载完成再进行替换
            this.c_maxConn_map = c_maxConn_map;
            this.c_window_map = c_window_map;
            this.c_publicKey_map = c_publicKey_map;
            this.c_ip_set = c_ip_set;
        }
        
        logger.info("### " + this + " refresh succeed!");
    }

    /*
     * (non-Javadoc)
     *
     * @see com.iwechat.service.ClientFactory#getMaxConnByClientId(java.lang.String)
     */
    @Override
    public int getMaxConnByClientId(String clientId) {
        Integer ret = c_maxConn_map.get(clientId);
        if (ret != null) {
            return ret.intValue();
        } else {
            return 0;
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.iwechat.service.ClientFactory#getWindowByClientId(java.lang.String)
     */
    @Override
    public int getWindowByClientId(String clientId) {
        Integer ret = c_window_map.get(clientId);
        if (ret != null) {
            return ret.intValue();
        } else {
            return 0;
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.iwechat.service.ClientFactory#getPublicStringByClientId(java.lang.String)
     */
    @Override
    public String getPublicStringByClientId(String clientId) {
        String ret = c_publicKey_map.get(clientId);
        if (ret != null) {
            return ret;
        } else {
            return "";
        }
    }

    @Override
    public boolean checkIP(String ip) {
        boolean ret = false;
        if (StringUtils.isEmpty(ip)) {
            ret = false;
        } else if (c_ip_set.contains(ip)) {
            ret = true;
        } else {
            for (String s : c_ip_set) {
                if (s.contains("/")) {
                    // s 为网段
                    ret = isInRange(ip, s);
                    if (ret) {
                        break;
                    }
                }
            }
        }
        return ret;
    }

    @Override
    public boolean isInRange(String ip, String cidr) {
        // ip
        String[] ips = ip.split("\\.");
        if (ips.length != 4) {
            return false;
        }
        int p1, p2, p3, p4;
        try {
            p4 = Integer.parseInt(ips[0]);
            p3 = Integer.parseInt(ips[1]);
            p2 = Integer.parseInt(ips[2]);
            p1 = Integer.parseInt(ips[3]);
            if (p4 > 254 || p3 > 254 || p2 > 254 || p1 > 254) {
                return false;
            }
        } catch (NumberFormatException e) {
            return false;
        }

        int ipAddr = (p4 << 24) | (p3 << 16) | (p2 << 8) | p1;

        // cidr
        int type = Integer.parseInt(cidr.replaceAll(".*/", ""));
        if (type > 32) {
            return false;
        }
        int mask = 0xFFFFFFFF << (32 - type);

        String cidrIp = cidr.replaceAll("/.*", "");
        String[] cidrIps = cidrIp.split("\\.");
        if (cidrIps.length != 4) {
            return false;
        }
        try {
            p4 = Integer.parseInt(cidrIps[0]);
            p3 = Integer.parseInt(cidrIps[1]);
            p2 = Integer.parseInt(cidrIps[2]);
            p1 = Integer.parseInt(cidrIps[3]);
            if (p4 > 254 || p3 > 254 || p2 > 254 || p1 > 254) {
                return false;
            }
        } catch (NumberFormatException e) {
            return false;
        }

        int cidrIpAddr = (p4 << 24) | (p3 << 16) | (p2 << 8) | p1;

        // compare
        return (ipAddr & mask) == (cidrIpAddr & mask);
    }

    @PostConstruct
    public void init() {
        logger.info("### " + this + " init succeed!");
    }
}
